//-------------------------------------------------------------------------------------------------
// Copyright (c) Bradford W. Mott and Flare Contributors
// North Carolina State University, Department of Computer Science
// The IntelliMedia Group
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//-------------------------------------------------------------------------------------------------

using UnityEngine;
using UnityEditor;
using Flare.Display;
using Flare.Geom;
using System.Collections.Generic;

/// <summary>
/// The SwfAssetEditor class displays the selected SWF asset version,
/// the SWF byte size, and the SWF linkages present in the SWF file.
/// The current limitation for this code is that it only
/// displays information for the current application domain.
/// </summary>
[CustomEditor(typeof(SwfAsset))]
public class SwfAssetEditor : Editor
{
    private List<object> symbolData = new List<object>();
    private List<TimelineScenes> mainTimelineData;
    private int totalScenes = 0;
    private bool[] symbolDetailsShown;
    PrintTimelineInfo[] printList;
   
    private Vector2 scrollPositionMain = new Vector2();

    private bool showInfo = false;
    private TimelineEditorInfo timelineInfo = new TimelineEditorInfo();
    private NativeWindow swfData;
    private bool showMainTimelineInfo = false;
    private MovieClip clip;

    /// <summary>
    /// Displays SWF asset information in the inspector
    /// </summary>
    public override void OnInspectorGUI()
    {
        SwfAsset swfAsset = target as SwfAsset;

        EditorGUILayout.LabelField("SwfAsset Version: " + swfAsset.Version);
        EditorGUILayout.LabelField("SwfAsset Size: " + swfAsset.Bytes.Length + " bytes");       

        if (showInfo)
        {
            showInfo = !GUILayout.Button("Click to hide SWF details");

            // Prevents the data from loading more than once but still allows the SWF details to be shown and hidden.
            if (mainTimelineData == null)
            {
                swfData = new NativeWindow(new Rectangle(), false, swfAsset.Bytes,null);
                clip = swfData.stage.GetChildByName("root1") as MovieClip;
                mainTimelineData = timelineInfo.GetTimelineInfo(clip);

                string[] names = swfData.applicationDomain.GetNames();
                symbolDetailsShown = new bool[names.Length];
                printList = new PrintTimelineInfo[names.Length + 1];

                for (int iteration = 0; iteration < names.Length; iteration++)
                {
                    symbolDetailsShown[iteration] = false;
                    printList[iteration] = new PrintTimelineInfo();
                    symbolData.Add(names[iteration]);
                    symbolData.Add(swfData.applicationDomain.GetDefinition(names[iteration]));
                }

                printList[symbolData.Count / 2] = new PrintTimelineInfo();

                SortSwfData();
            }

            scrollPositionMain = EditorGUILayout.BeginScrollView(scrollPositionMain);
            EditorGUI.indentLevel++;
            showMainTimelineInfo = EditorGUILayout.Foldout(showMainTimelineInfo, "Main Timeline Info");
            
            if (showMainTimelineInfo)
            {
                EditorGUI.indentLevel++;
                EditorGUILayout.LabelField("Number of frames in SWF file: " + totalScenes);
                totalScenes = 0;
                
                printList[0].Info(mainTimelineData, totalScenes);
                EditorGUI.indentLevel--;
            }
            
            if (symbolData.Count > 0)
            {
                EditorGUILayout.LabelField("");
                
                DisplayExportedSymbols(symbolData);
            }
            else
            {
                EditorGUI.indentLevel--;
                EditorGUILayout.LabelField("");
                EditorGUILayout.LabelField("(No Exported Symbols in SWF Asset)");
            }
            EditorGUILayout.EndScrollView();
            EditorGUI.indentLevel--;
        }
        else
        {
            showInfo = GUILayout.Button("Click to show SWF details");
        }
    }

    /// <summary>
    /// Sorts the exported SWF symbols in acending order.
    /// </summary>
    private void SortSwfData()
    {
        List<object> symbolHold = new List<object>();
        List<object> sortedSymbols = new List<object>();

        for (int iteration = 0; iteration < symbolData.Count; iteration += 2)
        {
            symbolHold.Add(symbolData[iteration]);
        }

        symbolHold.Sort();

        for (int iteration = 0; iteration < symbolHold.Count; iteration++)
        {
            for (int a = 0; a < symbolData.Count; a += 2)
            {
                if (symbolData[a] == symbolHold[iteration])
                {
                    sortedSymbols.Add(symbolHold[iteration]);
                    sortedSymbols.Add(symbolData[a + 1]);
                }
            }
        }
        symbolData = new List<object>();
        symbolData = sortedSymbols;
    }

    private void DisplayExportedSymbols(List<object> outputList)
    {
        // display all exported symbols
        for (int iteration = 0; iteration < outputList.Count; iteration += 2)
        {      
            symbolDetailsShown[iteration / 2] = EditorGUILayout.Foldout(symbolDetailsShown[iteration / 2],
                    "  " + outputList[iteration].ToString());
            EditorGUI.indentLevel++;
           
            if (symbolDetailsShown[iteration / 2])
            {
                if (outputList[iteration + 1] is Flare.Display.MovieClip)
                {
                    clip = swfData.applicationDomain.InstantiateDefinition<MovieClip>(outputList[iteration].ToString());
                    TimelineEditorInfo a = new TimelineEditorInfo();

                    List<TimelineScenes> newData = new List<TimelineScenes>();
                    newData.Add(a.GetTimelineInfo(clip)[0]);

                    Flare.Display.MovieClip temp = new Flare.Display.MovieClip();

                    temp = (Flare.Display.MovieClip) outputList[iteration + 1];
                    
                    EditorGUILayout.LabelField("Number of Frames: " + temp.totalFrames);

                    printList[iteration / 2 + 1].Info(newData, totalScenes);
                }
                else if (outputList[iteration + 1] is Flare.Media.Sound)
                {
                    EditorGUI.indentLevel++;
                    EditorGUILayout.LabelField("Sound");
                    EditorGUI.indentLevel--;
                }
                else
                {
                    EditorGUI.indentLevel++;
                    EditorGUILayout.LabelField("(Symbol is not a MovieClip or Sound)");
                    EditorGUI.indentLevel--;
                }
            }
            EditorGUI.indentLevel--;
        }
    }

    public override bool HasPreviewGUI()
    {
        return false;
    }
}

public class PrintTimelineInfo
{
    bool framesShown = false;
    bool symbolsShown = false;
    bool scriptsShown = false;

    public void Info(List<TimelineScenes> timelineData, int totalScenes)
    {
        int sceneCount = 1;
        int animationListLocation = 0;

        foreach (TimelineScenes scene in timelineData)
        {
            totalScenes += scene.framesInScene;
            
            int[] frameNumbers = scene.GetFrameNumbers();
            string[] frameNames = scene.GetFrameNames();
            
            string[] symbolNames = scene.GetSymbolNames();
            int[] symbolCreated = scene.GetSymbolCreatedFrame();
            int[] symbolDeleted = scene.GetSymbolDeletedFrame();
            int[] animationList = scene.GetAnimations();
            
            int[] scriptFrames = scene.GetScriptFrames();              
            
            int symbolIteration;
            
            if (timelineData.Count > 1)
            {
                EditorGUILayout.LabelField("Scene Name: " + scene.name + 
                                           "\t(scene number: " + sceneCount++ + 
                                           " total frames: " + scene.framesInScene + ")");
            }
            
            //---------------------------------------------------------------------------------------
            
            this.framesShown = EditorGUILayout.Foldout(this.framesShown, "Named Frames: (" + frameNames.Length + ")");
            
            if (this.framesShown)
            {
                if (frameNames.Length > 0)
                {
                    EditorGUI.indentLevel++;
                    EditorGUILayout.LabelField("Number\t\tName");
                    
                    for (symbolIteration = 0; symbolIteration < frameNames.Length; symbolIteration++)
                    {
                        string tempFrameName = frameNames[symbolIteration];
                        
                        EditorGUILayout.LabelField(frameNumbers[symbolIteration].ToString() + "\t\t\t\t" + tempFrameName);
                    }
                    
                    EditorGUI.indentLevel--;
                }
                else
                {
                    EditorGUILayout.LabelField("(NO NAMED FRAMES IN CURRENT SCENE)");
                }
            }
            
            //---------------------------------------------------------------------------------------               
            
            this.symbolsShown = EditorGUILayout.Foldout(this.symbolsShown, "Timeline Objects: (" + symbolNames.Length + ")");
            
            if (this.symbolsShown)
            {
                if (symbolNames.Length > 0)
                {
                    EditorGUI.indentLevel++;
                    
                    for (symbolIteration = 0; symbolIteration < symbolNames.Length; symbolIteration++)
                    {
                        string tempSymbolName = symbolNames[symbolIteration];
                        
                        EditorGUILayout.LabelField(tempSymbolName, EditorStyles.boldLabel);
                        
                        EditorGUI.indentLevel++;               
                        
                        if (totalScenes != scene.framesInScene &&
                            symbolCreated[symbolIteration] > scene.framesInScene)
                        {
                            EditorGUILayout.LabelField("Created Frame: " + 
                                                       (symbolCreated[symbolIteration] - (totalScenes - scene.framesInScene)));
                        }
                        else                 
                        {
                            EditorGUILayout.LabelField("Created Frame: " +
                                                       (totalScenes - scene.framesInScene + symbolCreated[symbolIteration]));
                        }
                        EditorGUILayout.LabelField("Deleted Frame: " + symbolDeleted[symbolIteration]);
                        
                        EditorGUILayout.LabelField("Frames Animated: ");
                        EditorGUI.indentLevel++;
                        
                        while (animationList[animationListLocation] != 0)
                        {
                            EditorGUILayout.LabelField(animationList[animationListLocation] + " - "
                                                       + animationList[animationListLocation + 1]);
                            animationListLocation += 2;
                        }
                        
                        EditorGUI.indentLevel -= 2;
                        animationListLocation++;
                    }
                    
                    EditorGUI.indentLevel--;
                }
                else
                {
                    EditorGUILayout.LabelField("(NO SYMBOLS IN SWF FILE)");
                }
            }
            
            //---------------------------------------------------------------------------------------
            
            this.scriptsShown = EditorGUILayout.Foldout(this.scriptsShown, "Scripts: (" + scriptFrames.Length + ")");
            
            if (this.scriptsShown)
            {
                if (scriptFrames.Length > 0)
                {                       
                    EditorGUI.indentLevel++;
                    for (symbolIteration = 0; symbolIteration < scriptFrames.Length; symbolIteration++)
                    {
                        EditorGUILayout.LabelField("Frame: " + scriptFrames[symbolIteration].ToString());
                        string[] scriptContents = scene.scriptContents(symbolIteration);
                        
                        EditorGUI.indentLevel++;
                        EditorGUILayout.LabelField("SCRIPT CONTENTS:");
                        EditorGUI.indentLevel++;
                        
                        for (int steps = 0; steps < scriptContents.Length; steps++)
                        {
                            EditorGUILayout.LabelField(scriptContents[steps]);
                        }
                        EditorGUI.indentLevel -= 2;
                    }
                    EditorGUI.indentLevel--;
                }
                else
                {
                    EditorGUILayout.LabelField("(NO AS2 ACTIONSCRIPTS IN CURRENT SCENE)");
                }
            }
        } 
    }
}